/* 
 * Copyright 2011 kOSMik.
 * This file is part of kOSMik.
 * 
 * kOSMik is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * kOSMik is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with kOSMik.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * kOSMik:
 * Open Street Map - Editor.
 * http://code.google.com/p/swp-dv-ws2010-osm-2/
 */

package de.fuberlin.kosmik.data.osmt;

import java.util.HashMap;

import android.content.ContentValues;
import android.database.Cursor;
import de.fuberlin.kosmik.StrUtils;

/**
 * This class maps a row of the FeatureLangText table to an object.
 * 
 * @author Johannes
 * 
 */
public class OsmtFeatureLangText implements OsmtTable {

	/** Name of table FeatureLangText. */
	static final String TABLE = "FeatureLangText";
	/** 1. Column name. */
	static final String COLUMN_KEY_NAME = "FeatureLangText.key_name";
	/** 2. Column name. */
	static final String COLUMN_VALUE_NAME = "FeatureLangText.value_name";
	/** 3. Column name. */
	static final String COLUMN_LANGUAGE = "FeatureLangText.language";
	/** 4. Column name. */
	static final String COLUMN_COMMENT = "FeatureLangText.comment";
	private final OsmtDatabase osmtDatabase;
	private final OsmtFeatureLangTextRecord record = new OsmtFeatureLangTextRecord();

	/**
	 * Constructor.
	 * <p>
	 * Creates an row representing object. Checks, if this row already exists in
	 * database. In this case, it fills the null column values and overrides the
	 * not identifying columns with the loaded data. If the row does not already
	 * exists, the data will be written to the database immediately.
	 * 
	 * @param osmtData Access to the database.
	 * @param key The value for the key. The key is identifying.
	 * @param value The value for the value-column. The value-column is
	 *            identifying.
	 * @param language The value for the language-column. The language-column is
	 *            identifying.
	 * @param comment Any comment in the passed language
	 * @throws OsmtObjectException Will be thrown, if the object cannot created
	 *             caused by a database error.
	 */
	public OsmtFeatureLangText(OsmtData osmtData, String key, String value, String language,
			String comment) throws OsmtObjectException {
		super();
		this.osmtDatabase = ((OsmtDataImpl) osmtData).getDatabase();
		if (!set(key, value, language, comment)) {
			throw new OsmtObjectException(
					String.format("can neither load nor create %s comment for %s - %s.",
							language, key, value));
		}

	}

	/**
	 * Constructor.
	 * <p>
	 * Creates an row representing object. In addition, this method behaves
	 * equivalent to
	 * {@link #OsmtFeatureLangText(OsmtData, String, String, String, String)}.
	 * 
	 * @param osmtData Access to the database.
	 * @param featureLangTextRecord The record that embrace all necessary data.
	 * @throws OsmtObjectException if the object cannot created caused by a
	 *             database error.
	 */
	public OsmtFeatureLangText(OsmtData osmtData,
			OsmtFeatureLangTextRecord featureLangTextRecord) throws OsmtObjectException {
		this(osmtData, featureLangTextRecord.osmKey, featureLangTextRecord.osmValue,
				featureLangTextRecord.language, featureLangTextRecord.comment);
	}

	/**
	 * Sets the data of these language dependent attributes without detailed
	 * checking. The data is not initially written to the database.
	 * 
	 * @param featureLangTextRecord The record that contain the data to be set.
	 */
	void setTrusted(OsmtFeatureLangTextRecord featureLangTextRecord) {
		record.osmKey = featureLangTextRecord.osmKey;
		record.osmValue = featureLangTextRecord.osmValue;
		record.language = featureLangTextRecord.language;
		record.comment = featureLangTextRecord.comment;
	}

	/**
	 * Sets the data of these language dependent attributes without detailed
	 * checking. The data is not initially written to the database.
	 * <p>
	 * In addition, this method behaves equivalent to
	 * {@link #setTrusted(OsmtFeatureLangTextRecord)}.
	 * 
	 * @param osmKey The value for the key. The key is identifying.
	 * @param osmValue The value for the value-column. The value-column is
	 *            identifying.
	 * @param language The value for the language-column. The language-column is
	 *            identifying.
	 * @param comment Any comment in the passed language
	 * 
	 */
	void setTrusted(String osmKey, String osmValue, String language, String comment) {
		record.osmKey = osmKey;
		record.osmValue = osmValue;
		record.language = language;
		record.comment = comment;
	}

	/**
	 * Sets the data of these language dependent attributes of a feature direct.
	 * <p>
	 * In addition, this method behaves equivalent to
	 * {@link #set(OsmtFeatureLangTextRecord)}.
	 * 
	 * @param osmKey The value for the key. The key is identifying.
	 * @param osmValue The value for the value-column. The value-column is
	 *            identifying.
	 * @param language The value for the language-column. The language-column is
	 *            identifying.
	 * @param comment Any comment in the passed language
	 * @return True if the feature was loaded and could be updated as necessary.
	 *         Is also true if instead the feature as a new line could be
	 *         inserted into the database. Otherwise false.
	 * 
	 */
	public boolean set(String osmKey, String osmValue, String language, String comment) {
		if (loadFeatureCommentByKeyValueLang(this, osmKey, osmValue, language)) {
			OsmtFeatureLangText.updateFeatureComment(this, comment);
			return true;
		}
		record.osmKey = osmKey;
		record.osmValue = osmValue;
		record.language = language;
		record.comment = comment;
		return tryInsertAsNewFeatureComment(this);
	}

	/**
	 * Sets the data of these language dependent attributes of a feature direct.
	 * <p>
	 * Checks, if this row already exists in database. In this case, it fills
	 * the null column values and overrides the not identifying columns with the
	 * loaded data. If the row does not already exists, the data will be written
	 * to the database immediately.
	 * 
	 * @param featureLangTextRecord The record that contain the data to be set.
	 * @return True if the feature was loaded and could be updated as necessary.
	 *         Is also true if instead the feature as a new line could be
	 *         inserted into the database. Otherwise false.
	 */
	public boolean set(OsmtFeatureLangTextRecord featureLangTextRecord) {
		return set(featureLangTextRecord.osmKey, featureLangTextRecord.osmValue,
				featureLangTextRecord.language, featureLangTextRecord.comment);
	}

	private static void updateFeatureComment(final OsmtFeatureLangText featureLangText,
			final String comment) {
		final ContentValues contentValues = new ContentValues();

		if (comment != null && !comment.equals(featureLangText.record.comment)) {
			contentValues.put(COLUMN_COMMENT, comment);
		}

		if (contentValues.size() > 0) {
			final String whereClause = String.format("%s='%s' AND %s='%s'  AND %s='%s' ",
					COLUMN_KEY_NAME, featureLangText.record.osmKey, COLUMN_VALUE_NAME,
					featureLangText.record.osmValue, COLUMN_LANGUAGE,
					featureLangText.record.language);
			featureLangText.osmtDatabase.update(featureLangText, contentValues, whereClause,
					null);
		}

	}

	/**
	 * Asks the database for the key-value-associated comment in the preferred
	 * language. If there is no comment in this language, this method delivers
	 * the comment that match the applications language. If there is still no
	 * such comment, it try to find an English command. Otherwise any comment
	 * will returned.
	 * 
	 * @param osmtData The database context.
	 * @param osmKey The key
	 * @param osmValue The value
	 * @param preferedLanguage The language you prefer
	 * @return The most appropriate comment
	 */
	static String getBestFittingFeatureCommentByKeyValue(final OsmtData osmtData,
			final String osmKey, final String osmValue, String preferedLanguage) {

		final String lookupByKeyValue = String.format("%s='%s' AND %s='%s'", COLUMN_KEY_NAME,
				osmKey, COLUMN_VALUE_NAME, osmValue);
		final String[] selectionCols = new String[] { COLUMN_LANGUAGE, COLUMN_COMMENT };
		final OsmtQuery query = new OsmtQuery(TABLE, selectionCols, lookupByKeyValue);
		final HashMap<String, String> localizedComments = new HashMap<String, String>();
		final boolean featureFound = ((OsmtDataImpl) osmtData).getDatabase().query(query,
				new OnObtainCursorListener() {

					@Override
					public boolean onObtainCursor(Cursor cursor) {
						if (cursor.moveToFirst()) {
							do {
								localizedComments.put(cursor.getString(0), cursor.getString(1));
							} while (cursor.moveToNext());
							return true;
						}
						return false;
					}
				});
		// pick the comment that best fitting
		String result;
		if (featureFound) {
			if (preferedLanguage != null && localizedComments.containsKey(preferedLanguage)) {
				result = localizedComments.get(preferedLanguage);
				if (StrUtils.isNotEmpty(result))
					return result;
			}
			if (localizedComments.containsKey(((OsmtDataImpl) osmtData).getAppLanguage())) {
				result = localizedComments.get(((OsmtDataImpl) osmtData).getAppLanguage());
				if (StrUtils.isNotEmpty(result))
					return result;
			}
			if (localizedComments.containsKey(OsmtDataImpl.getDefaultLanguage())) {
				result = localizedComments.get(OsmtDataImpl.getDefaultLanguage());
				if (StrUtils.isNotEmpty(result))
					return result;
			}
			for (String language : localizedComments.keySet()) {
				return localizedComments.get(language);
			}
		}

		return "";
	}

	private static boolean loadFeatureCommentByKeyValueLang(
			final OsmtFeatureLangText featureLangText, final String osmKey,
			final String osmValue, final String language) {

		final String lookupByKeyValue = String
				.format("%s='%s' AND %s='%s'  AND %s='%s' ", COLUMN_KEY_NAME, osmKey,
						COLUMN_VALUE_NAME, osmValue, COLUMN_LANGUAGE, language);
		final String[] selectionCols = new String[] { COLUMN_COMMENT };
		final OsmtQuery query = new OsmtQuery(TABLE, selectionCols, lookupByKeyValue);
		final boolean featureFound = featureLangText.osmtDatabase.query(query,
				new OnObtainCursorListener() {

					@Override
					public boolean onObtainCursor(Cursor cursor) {
						if (cursor.moveToFirst()) {
							featureLangText.setTrusted(osmKey, osmValue, language,
									cursor.getString(0));
							return true;
						}
						return false;
					}
				});
		return featureFound;
	}

	private static boolean tryInsertAsNewFeatureComment(
			final OsmtFeatureLangText featureLangText) {
		final ContentValues contentValues = new ContentValues();
		contentValues.put(COLUMN_KEY_NAME, featureLangText.record.osmKey);
		contentValues.put(COLUMN_VALUE_NAME, featureLangText.record.osmValue);
		contentValues.put(COLUMN_LANGUAGE, featureLangText.record.language);
		contentValues.put(COLUMN_COMMENT, featureLangText.record.comment);
		return featureLangText.osmtDatabase.insertRecord(featureLangText, contentValues);
	}

	@Override
	public String getTableName() {
		return TABLE;
	}
}
